import type { AppContext } from "@api/ai/agents/config/shared";
import { tz } from "@date-fns/tz";
import { db } from "@midday/db/client";
import { getTrackerProjects, upsertTrackerEntries } from "@midday/db/queries";
import { getAppUrl } from "@midday/utils/envs";
import { formatDate } from "@midday/utils/format";
import { tool } from "ai";
import { formatDistance, setHours, setMinutes } from "date-fns";
import parseDuration from "parse-duration";
import { z } from "zod";

const createTrackerEntrySchema = z.object({
  projectName: z.string().nullable().optional().describe("Project name"),
  projectId: z.string().nullable().optional().describe("Project ID"),
  duration: z.string().describe("Duration (e.g. '8h', '2h 30m', '480m')"),
  date: z.string().nullable().optional().describe("Date (YYYY-MM-DD)"),
  description: z.string().nullable().optional().describe("Description"),
});

/**
 * Parse duration string to seconds using parse-duration library
 * Supports: "8h", "2h", "30m", "480m", "8.5h" (decimal hours), "2h 30m" (compound)
 */
function parseDurationToSeconds(durationStr: string): number {
  const trimmed = durationStr.trim();

  // Handle bare decimal numbers (e.g., "8.5" treated as hours)
  if (/^\d+\.?\d*$/.test(trimmed)) {
    const hours = Number.parseFloat(trimmed);
    if (!Number.isNaN(hours)) {
      return Math.round(hours * 3600);
    }
  }

  // Use parse-duration for all other formats
  const milliseconds = parseDuration(trimmed);
  if (milliseconds === null || milliseconds === undefined) {
    throw new Error(
      `Invalid duration format: ${durationStr}. Use formats like '8h', '2h 30m', '480m', or '8.5h'`,
    );
  }

  return Math.round(milliseconds / 1000);
}

export const createTrackerEntryTool = tool({
  description:
    "Create a time entry for a tracker project - supports finding projects by name and flexible duration formats.",
  inputSchema: createTrackerEntrySchema,
  execute: async function* (
    { projectName, projectId, duration, date, description },
    executionOptions,
  ) {
    const appContext = executionOptions.experimental_context as AppContext;
    const teamId = appContext.teamId as string;
    const userId = appContext.userId || null;
    const searchProjectName = projectName;

    if (!teamId) {
      yield {
        text: "Unable to create tracker entry: Team ID not found in context.",
      };
      return;
    }

    try {
      // Find project by name or use provided projectId
      let finalProjectId: string | null = projectId || null;

      if (!finalProjectId && searchProjectName) {
        // Search for project by name
        const projectsResult = await getTrackerProjects(db, {
          teamId,
          q: searchProjectName,
          pageSize: 5,
        });

        if (projectsResult.data.length === 0) {
          yield {
            text: `No project found matching "${searchProjectName}". Please check the project name or provide a project ID.`,
          };
          return;
        }

        // Use the first matching project
        finalProjectId = projectsResult.data[0]?.id || null;

        // If multiple matches, mention it but use the first one
        if (projectsResult.data.length > 1 && projectsResult.data[0]) {
          const projectNames = projectsResult.data
            .map((p) => p.name)
            .join(", ");
          yield {
            text: `Multiple projects found matching "${searchProjectName}". Using "${projectsResult.data[0].name}". Other matches: ${projectNames}`,
          };
        }
      }

      if (!finalProjectId) {
        yield {
          text: "Please provide either a projectName or projectId to create a time entry.",
        };
        return;
      }

      // Parse duration
      let durationSeconds: number;
      try {
        durationSeconds = parseDurationToSeconds(duration);
      } catch (error) {
        yield {
          text:
            error instanceof Error ? error.message : "Invalid duration format.",
        };
        return;
      }

      // Determine date (default to today)
      const entryDate = date || new Date().toISOString().split("T")[0];
      if (!entryDate) {
        yield {
          text: "Invalid date format.",
        };
        return;
      }

      // Calculate start and stop times (9 AM in user's timezone + duration)
      const userTimezone = appContext.timezone || "UTC";
      let startTime: Date;

      if (userTimezone && userTimezone !== "UTC") {
        try {
          // Create a date at 9 AM in the user's timezone
          const createTZDate = tz(userTimezone);
          const baseDate = createTZDate(new Date(`${entryDate}T00:00:00`));
          startTime = setMinutes(setHours(baseDate, 9), 0);
        } catch (error) {
          // Fallback to UTC if timezone conversion fails
          startTime = new Date(`${entryDate}T09:00:00.000Z`);
        }
      } else {
        // Use UTC if no timezone provided
        startTime = new Date(`${entryDate}T09:00:00.000Z`);
      }

      const stopTime = new Date(startTime.getTime() + durationSeconds * 1000);

      const startTimeISO = startTime.toISOString();
      const stopTimeISO = stopTime.toISOString();

      // Create the entry
      const result = await upsertTrackerEntries(db, {
        teamId,
        projectId: finalProjectId,
        start: startTimeISO,
        stop: stopTimeISO,
        dates: [entryDate],
        duration: durationSeconds,
        assignedId: userId,
        description: description || null,
      });

      if (!result || result.length === 0) {
        yield {
          text: "Failed to create tracker entry.",
        };
        return;
      }

      const entry = result[0];
      if (!entry) {
        yield {
          text: "Failed to create tracker entry.",
        };
        return;
      }

      const start = new Date(0);
      const end = new Date(durationSeconds * 1000);
      const formattedDuration = formatDistance(start, end, {
        includeSeconds: false,
      });
      const projectName = entry.trackerProject?.name || "Unknown";

      const response = `Successfully created time entry:\n\n**Project:** ${projectName}\n**Date:** ${formatDate(entryDate)}\n**Duration:** ${formattedDuration}\n**Description:** ${entry.description || "None"}`;

      yield {
        text: response,
        link: {
          text: "View tracker",
          url: `${getAppUrl()}/tracker`,
        },
      };
    } catch (error) {
      yield {
        text: `Failed to create tracker entry: ${error instanceof Error ? error.message : "Unknown error"}`,
      };
    }
  },
});
